package com.alliky.core.util;

import android.app.Dialog;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;

import com.alliky.core.R;

import java.util.ArrayList;

/**
 * ================================================
 *
 * @Description: 描述
 * @Author: wxianing
 * @Date: 2023/5/15 17:46
 * ================================================
 */
public class RxDialog<T extends ViewDataBinding> extends Dialog implements View.OnClickListener {

    /**
     * Dialog layout视图
     */
    private T mBinding;
    /**
     * 点击事件监听（不带layout视图）
     */
    private OnClickCallback mClickEvent;
    /**
     * 点击事件监听（带layout视图）
     */
    private OnBindingClickCallback<T> mBindingClickEvent;

    private RxDialog(@NonNull Context context) {
        super(context, R.style.DialogTransparent);
    }

    @SuppressWarnings("unchecked")
    private RxDialog(Context context, Builder builder) {
        this(context);
        mBinding = DataBindingUtil.inflate(getLayoutInflater(), builder.layoutId, null, false);
        setContentView(mBinding.getRoot());
        initSetting(builder.mDialogSet, builder.mCancelable, builder.canceledOnTouchOutside);
        intercept(builder.event);
        setClickEvent(builder.clickViews);
        mClickEvent = builder.mClickEvent;
        mBindingClickEvent = builder.mBindingClickEvent;
    }

    /**
     * 窗体的一些设置
     *
     * @param mDialogSet             窗体的设置类
     * @param mCancelable            是否可取消（按物理返回键）
     * @param canceledOnTouchOutside 是否可点击外部取消
     **/
    private void initSetting(DialogSet mDialogSet, boolean mCancelable, boolean canceledOnTouchOutside) {
        Window window = getWindow();
        WindowManager.LayoutParams lp = window.getAttributes();
        if (mDialogSet != null) {
            lp.width = mDialogSet.width;
            lp.height = mDialogSet.height;
            lp.gravity = mDialogSet.gravity;
            setCancelable(mDialogSet.mCancelable);
            setCanceledOnTouchOutside(mDialogSet.canceledOnTouchOutside);
        } else {
            lp.width = getContext().getResources().getDimensionPixelOffset(R.dimen.dp_300);
            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
            lp.gravity = Gravity.CENTER;
            setCancelable(mCancelable);
            setCanceledOnTouchOutside(canceledOnTouchOutside);
        }
        window.setAttributes(lp);
    }

    /**
     * 设置点击事件
     *
     * @param clickViews 需要设置点击事件监听的View的id集合
     */
    private void setClickEvent(ArrayList<Integer> clickViews) {
        if (clickViews != null && mBinding != null) {
            for (int id : clickViews) {
                if (mBinding.getRoot().findViewById(id) != null) {
                    mBinding.getRoot().findViewById(id).setOnClickListener(this);
                }
            }
        }
    }

    /**
     * 拦截，可根据返回的ViewDataBinding实现需要的逻辑
     *
     * @param event 回调事件，可返回当前Dialog视图
     */
    private void intercept(GeneralDialogEvent<T> event) {
        if (event != null && mBinding != null) {
            event.getView(mBinding);
        }
    }

    /**
     * 设置点击事件回调
     *
     * @param event 回调事件
     */
    private void clickCallback(OnClickCallback event) {
        if (event != null && mBinding != null) {
            mClickEvent = event;
        }
    }

    /**
     * 设置点击事件回调
     *
     * @param event 回调事件
     */
    private void bindingClickCallback(OnBindingClickCallback<T> event) {
        if (event != null && mBinding != null) {
            mBindingClickEvent = event;
        }
    }

    @Override
    public void onClick(View v) {
        if (mClickEvent != null) {
            mClickEvent.onClick(this, v);
        }
        if (mBindingClickEvent != null) {
            mBindingClickEvent.onClick(this, mBinding, v);
        }
    }

    public interface GeneralDialogEvent<T> {
        /**
         * 返回Dialog的layout视图（DataBinding类型）
         */
        void getView(T mBinding);
    }

    public interface OnClickCallback {
        /**
         * 点击事件回调
         *
         * @param dialog dialog本身
         * @param v      产生点击事件的View
         */
        void onClick(RxDialog dialog, View v);
    }

    public interface OnBindingClickCallback<T> {
        /**
         * 点击事件回调
         *
         * @param dialog   dialog本身
         * @param mBinding dialog的layout视图（DataBinding类型）
         * @param v        产生点击事件的View
         */
        void onClick(RxDialog dialog, T mBinding, View v);
    }

    public static class Builder<T extends ViewDataBinding> {
        /**
         * Dialog的布局文件id
         */
        private int layoutId;
        /**
         * 用于返回Dialog的layout视图（DataBinding类型）
         */
        private GeneralDialogEvent event;
        /**
         * 点击事件监听（不带layout视图）
         */
        private OnClickCallback mClickEvent;
        /**
         * 点击事件监听（带layout视图）
         */
        private OnBindingClickCallback mBindingClickEvent;
        /**
         * 设置此对话框在触摸窗口外时是否被取消，优先级比cancel高
         */
        private DialogSet mDialogSet;
        /**
         * 设置此对话框在触摸窗口外时是否被取消，优先级比mDialogSet低
         */
        private boolean canceledOnTouchOutside = true;
        /**
         * 设置此对话框在能否被按返回键销毁，优先级比mDialogSet低
         */
        private boolean mCancelable;
        /**
         * 存储所有的带点击事件的View id集合
         */
        private ArrayList<Integer> clickViews;

        /**
         * 设置Dialog的布局文件id
         *
         * @param layoutId 布局文件id
         */
        public Builder layoutId(int layoutId) {
            this.layoutId = layoutId;
            return this;
        }

        /**
         * 设置Dialog的宽
         *
         * @param width 宽
         */
        public Builder width(int width) {
            if (mDialogSet == null) {
                mDialogSet = new DialogSet();
            }
            mDialogSet.width = width;
            return this;
        }

        /**
         * 设置Dialog的高
         *
         * @param height 高
         */
        public Builder height(int height) {
            if (mDialogSet == null) {
                mDialogSet = new DialogSet();
            }
            mDialogSet.height = height;
            return this;
        }

        /**
         * 设置Dialog的对齐方式
         *
         * @param gravity 对齐方式
         */
        public Builder gravity(int gravity) {
            if (mDialogSet == null) {
                mDialogSet = new DialogSet();
            }
            mDialogSet.gravity = gravity;
            return this;
        }

        /**
         * 设置Dialog点击外部是否可以取消
         *
         * @param cancel 是否可取消
         */
        public Builder setCanceledOnTouchOutside(boolean cancel) {
            if (mDialogSet == null) {
                mDialogSet = new DialogSet();
            }
            mDialogSet.canceledOnTouchOutside = cancel;
            this.canceledOnTouchOutside = cancel;
            return this;
        }

        /**
         * 设置Dialog是否可以取消（按物理返回键）
         *
         * @param cancel 是否可取消
         */
        public Builder setCancelable(boolean cancel) {
            if (mDialogSet == null) {
                mDialogSet = new DialogSet();
            }
            mDialogSet.mCancelable = cancel;
            this.mCancelable = cancel;
            if (!cancel) {
                /*this.canceledOnTouchOutside默认为true，当设置mCancelable为false时，设置canceledOnTouchOutside会影响
                 * mCancelable的值，所以如果设置了mCancelable为false，则canceledOnTouchOutside先要设置为false
                 * */
                mDialogSet.canceledOnTouchOutside = false;
                this.canceledOnTouchOutside = false;
            }
            return this;
        }

        /**
         * 拦截事件
         */
        public Builder intercept(GeneralDialogEvent event) {
            this.event = event;
            return this;
        }

        /**
         * 点击事件回调
         */
        public Builder callbackClickOn(OnClickCallback event) {
            this.mClickEvent = event;
            return this;
        }

        /**
         * 点击事件回调
         */
        public Builder bindingCallbackClickOn(OnBindingClickCallback event) {
            this.mBindingClickEvent = event;
            return this;
        }

        /**
         * 增加点击事件
         *
         * @param id 点击监听的View的id
         */
        public Builder addClickView(int id) {
            if (clickViews == null) {
                clickViews = new ArrayList<>();
            }
            clickViews.add(id);
            return this;
        }

        /**
         * 增加点击事件
         *
         * @param ids 点击监听的Views的id
         */
        public Builder addClickViews(int... ids) {
            if (ids != null && ids.length > 0) {
                if (clickViews == null) {
                    clickViews = new ArrayList<>();
                }
                for (int id : ids) {
                    clickViews.add(id);
                }
            }

            return this;
        }

        /**
         * 返回带DialogSet的GeneralDialog
         *
         * @param context 上下文
         */
        public RxDialog Build(Context context) {
            if (mDialogSet == null) {
                throw new RuntimeException("未设置窗体大小和位置信息");
            }
            return new RxDialog<T>(context, this);
        }

        /**
         * 返回不带DialogSet的GeneralDialog
         *
         * @param context 上下文
         */
        public RxDialog getDefault(Context context) {
            if (mDialogSet != null) {
                mDialogSet = null;
            }
            return new RxDialog<T>(context, this);
        }
    }

    /**
     * 窗体设置类
     */
    public static class DialogSet {
        /**
         * Dialog的宽
         */
        private int width;
        /**
         * Dialog的高
         */
        private int height;
        /**
         * Dialog对齐模式
         */
        private int gravity;
        /**
         * 点击外部是否能取消
         */
        boolean canceledOnTouchOutside;
        /**
         * 是否能取消
         */
        boolean mCancelable;
    }
}
